#include <stdint.h>
#include <string.h>

/* HAL */
#include "boards.h"
#include "simple_hal.h"
#include "app_timer.h"
#include "nrf_serial.h"

/* Core */
#include "nrf_mesh_config_core.h"
#include "nrf_mesh_gatt.h"
#include "nrf_mesh_configure.h"
#include "nrf_mesh.h"
#include "mesh_stack.h"
#include "device_state_manager.h"
#include "access_config.h"
#include "access_reliable.h"

/* Provisioning and configuration */
#include "mesh_provisionee.h"
#include "mesh_app_utils.h"

/* Models */
#include "simple_on_off_client.h"

/* Logging and RTT */
#include "log.h"
#include "rtt_input.h"

/* Example specific includes */
#include "app_config.h"
#include "nrf_mesh_config_examples.h"
#include "light_switch_example_common.h"
#include "example_common.h"
#include "ble_softdevice_support.h"

/*****************************************************************************
 * Definitions
 *****************************************************************************/
#define APP_STATE_OFF (0)
#define APP_STATE_ON (1)

#define APP_UNACK_MSG_REPEAT_COUNT (2)

/* Controls if the model instance should force all mesh messages to be segmented messages. */
#define APP_FORCE_SEGMENTATION (false)
/* Controls the MIC size used by the model instance for sending the mesh messages. */
#define APP_MIC_SIZE (NRF_MESH_TRANSMIC_SIZE_SMALL)
/* Delay value used by the OnOff client for sending OnOff Set messages. */
#define APP_ONOFF_DELAY_MS (50)
/* Transition time value used by the OnOff client for sending OnOff Set messages. */
#define APP_ONOFF_TRANSITION_TIME_MS (100)

APP_TIMER_DEF(m_uart_timer);

static void app_generic_onoff_client_status_cb(const simple_on_off_client_t *p_self, simple_on_off_status_t status, uint16_t src);

static simple_on_off_client_t m_onoff_client = {
    .status_cb = app_generic_onoff_client_status_cb
    // .timeout_cb = ;
};

// UART config
static void sleep_handler(void)
{
    __WFE();
    __SEV();
    __WFE();
}

NRF_SERIAL_DRV_UART_CONFIG_DEF(m_uart0_drv_config, 26, 25, 0, 0,
                               NRF_UART_HWFC_ENABLED, NRF_UART_PARITY_EXCLUDED,
                               NRF_UART_BAUDRATE_115200,
                               7);

NRF_SERIAL_QUEUES_DEF(serial_queues, 32, 32);

NRF_SERIAL_BUFFERS_DEF(serial_buffs, 1, 1);

NRF_SERIAL_CONFIG_DEF(serial_config, NRF_SERIAL_MODE_POLLING,
                      &serial_queues, &serial_buffs, NULL, sleep_handler);

NRF_SERIAL_UART_DEF(serial_uart, 0);

static bool m_device_provisioned;

// 单播地址打印
static void unicast_address_print(void)
{
    dsm_local_unicast_address_t node_address;
    dsm_local_unicast_addresses_get(&node_address);
    __LOG(LOG_SRC_APP, LOG_LEVEL_INFO, "Node Address: 0x%04x \n", node_address.address_start);
}

// 配网完成回调
static void provisioning_complete_cb(void)
{
    __LOG(LOG_SRC_APP, LOG_LEVEL_INFO, "Successfully provisioned\n");

    unicast_address_print();
    hal_led_blink_stop();
    hal_led_mask_set(HAL_LED_MASK, LED_MASK_STATE_OFF);
    hal_led_blink_ms(HAL_LED_MASK, LED_BLINK_INTERVAL_MS, LED_BLINK_CNT_PROV);
}

/* 收到服务端消息的回调 */
static void app_generic_onoff_client_status_cb(const simple_on_off_client_t *p_self, simple_on_off_status_t status, uint16_t src)
{
    (void)p_self;

    __LOG(LOG_SRC_APP, LOG_LEVEL_INFO, "element: 0x%X, status: %d\n", src, status);
}

// 重置节点
static void node_reset(void)
{
    __LOG(LOG_SRC_APP, LOG_LEVEL_INFO, "----- Node reset  -----\n");
    hal_led_blink_ms(HAL_LED_MASK, LED_BLINK_INTERVAL_MS, LED_BLINK_CNT_RESET);
    /* This function may return if there are ongoing flash operations. */
    mesh_stack_device_reset();
}

// 配网器事件回调
static void config_server_evt_cb(const config_server_evt_t *p_evt)
{
    // 重置
    if (p_evt->type == CONFIG_SERVER_EVT_NODE_RESET)
    {
        node_reset();
    }
}

#if NRF_MESH_LOG_ENABLE
static const char m_usage_string[] =
    "\n"
    "\t\t------------------------------------------------------------------------------------\n"
    "\t\t Button/RTT 1) Send a message to the odd group (address: 0xC003) to turn on LED 1.\n"
    "\t\t Button/RTT 2) Send a message to the odd group (address: 0xC003) to turn off LED 1.\n"
    "\t\t Button/RTT 3) Send a message to the even group (address: 0xC002) to turn on LED 1.\n"
    "\t\t Button/RTT 4) Send a message to the even group (address: 0xC002) to turn off LED 1.\n"
    "\t\t------------------------------------------------------------------------------------\n";
#endif

// 按钮处理
static void button_event_handler(uint32_t button_number)
{
    /* Increase button number because the buttons on the board is marked with 1 to 4 */
    button_number++;
    __LOG(LOG_SRC_APP, LOG_LEVEL_INFO, "Button %u pressed\n", button_number);

    uint32_t status = NRF_SUCCESS;

    (void)access_model_reliable_cancel(m_onoff_client.model_handle);
    switch (button_number)
    {
    case 1:
        status = simple_on_off_client_set(&m_onoff_client, true);
        hal_led_pin_set(BSP_LED_0, 1);
        break;

    case 2:
        status = simple_on_off_client_set(&m_onoff_client, false);
        hal_led_pin_set(BSP_LED_0, 0);
        break;
    }

    switch (status)
    {
    case NRF_SUCCESS:
        break;

    case NRF_ERROR_NO_MEM:
    case NRF_ERROR_BUSY:
    case NRF_ERROR_INVALID_STATE:
        __LOG(LOG_SRC_APP, LOG_LEVEL_INFO, "Client %u cannot send\n", button_number);
        hal_led_blink_ms(HAL_LED_MASK, LED_BLINK_SHORT_INTERVAL_MS, LED_BLINK_CNT_NO_REPLY);
        break;

    case NRF_ERROR_INVALID_PARAM:
        /* Publication not enabled for this client. One (or more) of the following is wrong:
             * - An application key is missing, or there is no application key bound to the model
             * - The client does not have its publication state set
             *
             * It is the provisioner that adds an application key, binds it to the model and sets
             * the model's publication state.
             */
        __LOG(LOG_SRC_APP, LOG_LEVEL_INFO, "Publication not configured for client %u\n", button_number);
        break;

    default:
        ERROR_CHECK(status);
        break;
    }
}

// RTT中断处理
static void rtt_input_handler(int key)
{
    if (key >= '1' && key <= '4')
    {
        uint32_t button_number = key - '1';
        button_event_handler(button_number);
    }
    else
    {
        __LOG(LOG_SRC_APP, LOG_LEVEL_INFO, m_usage_string);
    }
}

// 串口处理
static void uart_handler(void *point)
{
    uint32_t key = *(uint32_t *)point;

    button_event_handler(key);
}

// 初始化模型
static void models_init_cb(void)
{
    __LOG(LOG_SRC_APP, LOG_LEVEL_INFO, "Initializing and adding models\n");

    ERROR_CHECK(simple_on_off_client_init(&m_onoff_client, 0));

    ERROR_CHECK(access_model_subscription_list_alloc(m_onoff_client.model_handle));

}

// 初始化mesh
static void mesh_init(void)
{
    mesh_stack_init_params_t init_params =
        {
            .core.irq_priority = NRF_MESH_IRQ_PRIORITY_LOWEST,
            .core.lfclksrc = DEV_BOARD_LF_CLK_CFG,
            .core.p_uuid = NULL,
            .models.models_init_cb = models_init_cb,
            .models.config_server_cb = config_server_evt_cb};

    uint32_t status = mesh_stack_init(&init_params, &m_device_provisioned);
    switch (status)
    {
    case NRF_ERROR_INVALID_DATA:
        __LOG(LOG_SRC_APP, LOG_LEVEL_INFO, "Data in the persistent memory was corrupted. Device starts as unprovisioned.\n");
        __LOG(LOG_SRC_APP, LOG_LEVEL_INFO, "Reboot device before starting of the provisioning process.\n");
        break;
    case NRF_SUCCESS:
        break;
    default:
        ERROR_CHECK(status);
    }
}

// 总初始化方法
static void initialize(void)
{
    // 日志模块初始化
    __LOG_INIT(LOG_SRC_APP | LOG_SRC_ACCESS | LOG_SRC_BEARER, LOG_LEVEL_INFO, LOG_CALLBACK_DEFAULT);
    __LOG(LOG_SRC_APP, LOG_LEVEL_INFO, "----- BLE Mesh Light Switch Client Demo -----\n");

    // 定时器初始化
    ERROR_CHECK(app_timer_init());

    // led初始化
    hal_leds_init();

    // 蓝牙栈初始化
    ble_stack_init();

    // mesh初始化
    mesh_init();

    uint32_t ret;

    // 串口初始化
    ret = nrf_serial_init(&serial_uart, &m_uart0_drv_config, &serial_config);
    APP_ERROR_CHECK(ret);

    // 串口定时器初始化
    ERROR_CHECK(app_timer_create(&m_uart_timer, APP_TIMER_MODE_SINGLE_SHOT, uart_handler));

    // 串口发送测试文字
    static char tx_message[] = "Hello nrf_serial!\n\r";

    ret = nrf_serial_write(&serial_uart,
                           tx_message,
                           strlen(tx_message),
                           NULL,
                           NRF_SERIAL_MAX_TIMEOUT);
    APP_ERROR_CHECK(ret);
}

static void start(void)
{
    rtt_input_enable(rtt_input_handler, RTT_INPUT_POLL_PERIOD_MS);

    // 检测是否配网
    if (!m_device_provisioned)
    {
        static const uint8_t static_auth_data[NRF_MESH_KEY_SIZE] = STATIC_AUTH_DATA;
        mesh_provisionee_start_params_t prov_start_params =
            {
                .p_static_data = static_auth_data,
                .prov_sd_ble_opt_set_cb = NULL,
                .prov_complete_cb = provisioning_complete_cb,
                .prov_device_identification_start_cb = NULL,
                .prov_device_identification_stop_cb = NULL,
                .prov_abort_cb = NULL,
                .p_device_uri = EX_URI_LS_CLIENT};
        ERROR_CHECK(mesh_provisionee_prov_start(&prov_start_params));
    }
    else
    {
        unicast_address_print();
    }

    // 打印UUID
    mesh_app_uuid_print(nrf_mesh_configure_device_uuid_get());

    // 启动mesh栈
    ERROR_CHECK(mesh_stack_start());

    __LOG(LOG_SRC_APP, LOG_LEVEL_INFO, m_usage_string);

    hal_led_mask_set(HAL_LED_MASK, LED_MASK_STATE_OFF);
    hal_led_blink_ms(HAL_LED_MASK, LED_BLINK_INTERVAL_MS, LED_BLINK_CNT_START);
}

int main(void)
{
    initialize();
    start();

    for (;;)
    {
        char c;
        uint32_t ret = nrf_serial_read(&serial_uart, &c, sizeof(c), NULL, 1000);
        if (ret != NRF_SUCCESS)
        {
            continue;
        }

        uint32_t key;

        switch (c)
        {
        case 'o':
            key = 0;
            ERROR_CHECK(app_timer_start(m_uart_timer, HAL_MS_TO_RTC_TICKS(1), &key));

            break;
        case 'c':
            key = 1;
            ERROR_CHECK(app_timer_start(m_uart_timer, HAL_MS_TO_RTC_TICKS(1), &key));

            break;

        default:
            break;
        }

        __LOG(LOG_SRC_APP, LOG_LEVEL_INFO, "%s\n", c);

        (void)sd_app_evt_wait();
    }
}
